﻿
//o3djs.require('o3djs.util');
//o3djs.require('o3djs.math');
//o3djs.require('o3djs.rendergraph');
//o3djs.require('o3djs.pack');
//o3djs.require('o3djs.camera');
//o3djs.require('o3djs.picking');
//o3djs.require('o3djs.scene');
//o3djs.require('o3djs.debug');

// Events
// init() once the page has finished loading.
// unload() when the page is unloaded.
//window.onload = init;
//window.onunload = unload;

// constants
var NORMAL_SCALE_FACTOR = 2.0;

// global variables
var g_o3d;
var g_math;
var g_client;
var g_pack;
var g_viewInfo;
var g_treeInfo;  // information about the transform graph.
var g_pickInfoElem;
var g_debugHelper;
var g_debugLineGroup;
var g_debugLine;
var g_selectedInfo = null;
var g_flashTimer = 0;
var g_highlightMaterial;
var g_highlightShape;
var g_finished = false;  // for selenium testing.

function updateInfo() {
  if (!g_treeInfo) {
    g_treeInfo = o3djs.picking.createTransformInfo(g_client.root,
                                                        null);
  }
  g_treeInfo.update();
}

function unSelectAll() {
  if (g_selectedInfo) {
    // Remove it from the transform of the selected object.
    g_selectedInfo.shapeInfo.parent.transform.removeShape(g_highlightShape);
    // Remove everything related to it.
    o3djs.shape.deleteDuplicateShape(g_highlightShape, g_pack);
    g_highlightShape = null;
    g_selectedInfo = null;
  }
}

function select(pickInfo) {
  unSelectAll();
  if (pickInfo) {
    g_selectedInfo = pickInfo;
    // make a copy of the selected shape so we can use it to highlight.
    g_highlightShape = o3djs.shape.duplicateShape(
        g_pack,
        g_selectedInfo.shapeInfo.shape,
        'highlight_');
    // Set all of it's elements to use the highlight material.
    var elements = g_highlightShape.elements;
    for (var ee = 0; ee < elements.length; ee++) {
      elements[ee].material = g_highlightMaterial;
    }

    // Add it to the same transform
    g_selectedInfo.shapeInfo.parent.transform.addShape(g_highlightShape);
    g_flashTimer = 0.0;  // make it change color immediately.
  }
}

function pick(e) {
  var worldRay = o3djs.picking.clientPositionToWorldRay(
      e.x,
      e.y,
      g_viewInfo.drawContext,
      g_client.width,
      g_client.height);
  unSelectAll();

  // Update the entire tree in case anything moved.
  // NOTE: This function is very SLOW!
  // If you really want to use picking you should manually update only those
  // transforms and shapes that moved, were added, or deleted by writing your
  // own picking library. You should also make sure that you are only
  // considering things that are pickable. By that I mean if you have a scene of
  // a meadow with trees, grass, bushes, and animals and the only thing the user
  // can pick is the animals then put the animals on their own sub branch of the
  // transform graph and only pick against that subgraph.
  // Even better, make a separate transform graph with only cubes on it to
  // represent the animals and use that instead of the actual animals.
  g_treeInfo.update();

  var pickInfo = g_treeInfo.pick(worldRay);
  if (pickInfo) {
    select(pickInfo);
    g_pickInfoElem.innerHTML = pickInfo.shapeInfo.shape.name;
    UltraApplication_set_current(pickInfo.shapeInfo.shape.name);
    // Display the normal
    // NOTE: Lookup the normal with this function is very SLOW!!
    // If you need performance, for a game or something, you should consider
    // other methods.
    var normal = o3djs.element.getNormalForTriangle(
        pickInfo.element,
        pickInfo.rayIntersectionInfo.primitiveIndex);

    // Convert the normal from local to world space.
    normal = g_math.matrix4.transformNormal(
        pickInfo.shapeInfo.parent.transform.worldMatrix,
        normal);

    // Remove the scale from the normal.
    normal = g_math.normalize(normal);

    // Get the world position of the collision.
    var worldPosition = pickInfo.worldIntersectionPosition;

    // Add the normal to it to get a point in space above it with some
    // multiplier to scale it.
    var normalSpot = g_math.addVector(
        worldPosition,
        g_math.mulVectorScalar(normal, NORMAL_SCALE_FACTOR));

    // Move our debug line to show the normal
    g_debugLine.setVisible(true);
    g_debugLine.setEndPoints(worldPosition, normalSpot);
  } else {
    g_debugLine.setVisible(false);
    g_pickInfoElem.innerHTML = '--nothing--';
  }
}

function onrender(renderEvent) {
  g_flashTimer += renderEvent.elapsedTime;
  g_flashTimer = g_flashTimer % 0.5;
  if (g_selectedInfo) {
//    if (g_flashTimer < 0.25) {
//      g_highlightMaterial.getParam('color').value = [1, 1, 1, 1];
//    } else {
//      g_highlightMaterial.getParam('color').value = [0, 0, 0, 1];
//    }
  }
}

/**
 * Loads a scene into the transform graph.
 * @param {!o3d.Pack} pack Pack to load scene into.
 * @param {string} fileName filename of the scene.
 * @param {!o3d.Transform} parent parent node in the transform graph to
 *      which to load the scene into.
 */
function loadScene(pack, fileName, parent) {
  // Get our full path to the scene
  var scenePath = o3djs.util.getCurrentURI() + fileName;

  // Load the file given the full path, and call the callback function
  // when its done loading.
  o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);

  /**
   * Our callback is called once the scene has been loaded into memory
   * from the web or locally.
   * @param {!o3d.Pack} pack The pack that was passed in above.
   * @param {!o3d.Transform} parent The parent that was passed in above.
   * @param {*} exception null if loading succeeded.
   */
  function callback(pack, parent, exception) {
    if (exception) {
      alert('Could not load: ' + fileName + '\n' + exception);
      return;
    }
    // Get a cameraInfo (an object with a view and projection matrix)
    // using our javascript library function
    var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
        parent,
        g_client.width,
        g_client.height);

    // Copy the one from the file to ours.
    g_viewInfo.drawContext.view = cameraInfo.view;
    g_viewInfo.drawContext.projection = cameraInfo.projection;

    // Generate draw elements and setup material draw lists.
    o3djs.pack.preparePack(pack, g_viewInfo);

    // Update our info
    updateInfo();

    g_treeInfo.dump('');

    g_finished = true;  // for selenium testing.
  }
}

/**
 * Creates the client area.
 */
function init() {
  o3djs.util.makeClients(initStep2);
}

/**
 * Initializes O3D and loads the scene into the transform graph.
 * @param {Array} clientElements Array of o3d object elements.
 */
function initStep2(clientElements) {
  // Initializes global variables and libraries.
  var o3dElement = clientElements[0];
  o3dElement.name = 'o3dObj';  // This is only for our selenium tests.
  g_o3d = o3dElement.o3d;
  g_math = o3djs.math;
  g_client = o3dElement.client;

  g_pickInfoElem = document.getElementById('pickInfo');

  // Creates a pack to manage our resources/assets
  g_pack = g_client.createPack();

  // Create the render graph for a view.
  g_viewInfo = o3djs.rendergraph.createBasicView(
      g_pack,
      g_client.root,
      g_client.renderGraphRoot);

  // Use the debug library to create a line we can position to show
  // the normal.
  g_debugHelper = o3djs.debug.createDebugHelper(g_client.createPack(),
                                                g_viewInfo);
  g_debugLineGroup = g_debugHelper.createDebugLineGroup(g_client.root);
  g_debugLine = g_debugLineGroup.addLine();
  g_debugLine.setColor([0,1,0,1]);

  // Create a material for highlighting.
  g_highlightMaterial = o3djs.material.createConstantMaterial(
      g_pack,
      g_viewInfo,
      [1, 1, 1, 1]);
  // Setup a state to bring the lines forward.
  var state = g_pack.createObject('State');
  state.getStateParam('PolygonOffset2').value = -1.0;
  state.getStateParam('FillMode').value = g_o3d.State.WIREFRAME;
  g_highlightMaterial.state = state;

  // Creates a transform to put our data on.
  var my_data_root = g_pack.createObject('Transform');

  // Connects our root to the client's root.
  my_data_root.parent = g_client.root;

  // Load the scene into the transform graph as a child of my_data_root
  loadScene(g_pack, 'assets/seven_shapes.o3dtgz', my_data_root);

  g_client.setRenderCallback(onrender);

  // Start picking; it won't do anything until the scene finishes loading.
  o3djs.event.addEventListener(o3dElement, 'mousedown', pick);
}

/**
 * Removes any callbacks so they don't get called after the page has unloaded.
 */
function unload() {
  if (g_client) {
    g_client.cleanup();
  }
}
